/* 
 * File:   msstpclient.cpp
 * Author: rajeeb
 *
 * Created on May 19, 2011, 11:29 AM
 */

/*
 ** client.c -- a stream socket client demo
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <iostream>
#include <fstream>

using namespace std;




#define HOST "okanagan.cs.ubc.ca"

#define PORT "12345" // the port client will be connecting to

#define MAXDATASIZE 100000 // max number of bytes we can get at once

// get sockaddr, IPv4 or IPv6:

void *get_in_addr(struct sockaddr *sa) {
    if (sa->sa_family == AF_INET) {
        return &(((struct sockaddr_in*) sa)->sin_addr);
    }

    return &(((struct sockaddr_in6*) sa)->sin6_addr);
}

int connectToServer(char *host, char *port) {
    int sockfd;
    struct addrinfo hints, *servinfo, *p;
    int status;
    char ipstr[INET6_ADDRSTRLEN];

    memset(&hints, 0, sizeof hints); // make sure the struct is empty
    hints.ai_family = AF_UNSPEC; // don't care IPv4 or IPv6
    hints.ai_socktype = SOCK_STREAM; // TCP stream sockets
    hints.ai_flags = AI_PASSIVE; // fill in my IP for me

    // get ready to connect
    if ((status = getaddrinfo(host, port, &hints, &servinfo)) != 0) {
        fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(status));
        return 1;
    }

    //   cout << "serverinfo " << servinfo->ai_family << endl;

    //     loop through all the results and connect to the first we can
    for (p = servinfo; p != NULL; p = p->ai_next) {
        if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol))
                == -1) {
            perror("client: socket");
            continue;
        }

        //     cout << "server info : " << servinfo << endl;
        //   cout << "sockfd " << sockfd << endl;


        if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
            close(sockfd);
            perror("client: connect");
            continue;
        }

        break;
    }

    //   cout << p << endl;

    if (p == NULL) {
        fprintf(stderr, "client: failed to connect\n");
        return 2;
    }

    inet_ntop(p->ai_family, get_in_addr((struct sockaddr *) p->ai_addr),
            ipstr, sizeof ipstr);
    printf("client: connecting to %s\n", ipstr);

    freeaddrinfo(servinfo); // free the linked list

    return sockfd;
}

void sendMsg(int sockfd, char msg[]) {
    int numbytes;
    if ((numbytes = send(sockfd, msg, strlen(msg), 0)) == -1) {
        perror("send");
        exit(1);
    }

    cout << "send: " << numbytes << endl;
}

void recvMsg(int sockfd, char buf[]) {
    int numbytes;
    bzero(buf, strlen(buf));

    if ((numbytes = recv(sockfd, buf, MAXDATASIZE - 1, 0)) == -1) {
        perror("recv");
        exit(1);
    }

    cout << "recv: " << numbytes << endl;

    buf[numbytes] = '\0';
}

int quitCommand(int sockfd, char *msg) {
    char buf[MAXDATASIZE];

    sendMsg(sockfd, msg);
    recvMsg(sockfd, buf);

    // printf("<-- %s", buf);

    if (!memcmp(buf, "S", 1)) {
        cout << "Connection: could not terminate" << endl;
        return -1;
    }

    cout << "Connection: terminated" << endl;
    return 0;
}

int pwdCommand(int sockfd, char *msg) {
    char buf[MAXDATASIZE];

    sendMsg(sockfd, msg);
    recvMsg(sockfd, buf);

    //    printf("<-- %s", buf);
    printf("<-- ");
    char *cmd = strtok(buf, " ");
    if (memcmp(cmd, "S", 1) == 0) {
        //printf("%s", buf);
        cmd = strtok(NULL, " ");
        while (cmd != NULL) {
            printf("%s", cmd);
            cmd = strtok(NULL, " ");
        }

    }

    return 0;
}

int cwdCommand(int sockfd, char *msg) {
    char buf[MAXDATASIZE];

    sendMsg(sockfd, msg);
    recvMsg(sockfd, buf);

    printf("<-- %s", buf);

    return 0;
}

int infoCommand(int sockfd, char *msg) {
    char buf[MAXDATASIZE];

    sendMsg(sockfd, msg);
    recvMsg(sockfd, buf);

    printf("<-- %s", buf);

    return 0;
}

int listCommand(int sockfd, char *msg) {
    char buf[MAXDATASIZE];

    sendMsg(sockfd, msg);
    recvMsg(sockfd, buf);

    printf("%s", buf);

    char *cmd = strtok(buf, " ");
    char *host;
    char *port;
    if (memcmp(cmd, "P", 1) == 0) {

        cmd = strtok(NULL, " ");

        host = (char*) malloc(strlen(cmd));
        memcpy(host, cmd, strlen(cmd) + 1);
        strcat(host, "\0");
        printf("host %s \n", host);
        cmd = strtok(NULL, " ");
        port = (char*) malloc(strlen(cmd));
        memcpy(port, cmd, strlen(cmd) + 1);
        strcat(port, "\0");
        printf("port %s \n", port);

        int dataSockFd = connectToServer(host, port);
        bzero(buf, MAXDATASIZE);

        recvMsg(dataSockFd, buf);
        printf("Data %s \n", buf);
        
    }


    return 0;
}

int retrCommand(int sockfd, char *msg) {
    char buf[MAXDATASIZE];

    sendMsg(sockfd, msg);
    recvMsg(sockfd, buf);

    printf("<-- %s", buf);

    return 0;
}

char findCommand(char* userInput) {
    char *input;
    input = (char*) malloc(strlen(userInput));
    bzero(input, strlen(input));
    memcpy(input, userInput, strlen(userInput) + 1);

    char *cmd = strtok(input, " ");
    // strupr(cmd);
    cout << "command: " << cmd << endl;

    if (strcmp(cmd, "QUIT") == 0) {
        return 'Q';
    }
    else if (strcmp(cmd, "PWD") == 0) {
        return 'P';
    }
    else if (strcmp(cmd, "CWD") == 0) {
        return 'C';
    }
    else if (strcmp(cmd, "INFO") == 0) {
        return 'I';
    }
    else if (strcmp(cmd, "LIST") == 0) {
        return 'L';
    }
    else if (strcmp(cmd, "RETR") == 0) {
        return 'R';
    }
    else {
        return 'Z'; // other command
    }
}

void stoupper(string& s) {
    string::iterator i = s.begin();
    string::iterator end = s.end();

    while (i != end) {
        *i = toupper((unsigned char)*i);
        ++i;
    }
}

int main(int argc, char *argv[]) {
    int sockfd;
    char msg [100], userInput[100], command;

    sockfd = connectToServer((char*)HOST, (char*)PORT);

    printf("\n");

    while (true) {
        printf("--> ");
        bzero(userInput, strlen(userInput));
        cin >> userInput;

        command = findCommand(userInput);
        cout << "cmd " << command << endl;

        if (command == 'Z') {
            continue;
        }

        strcat(userInput, "\n");

        memcpy(msg, userInput, strlen(userInput));

        switch (command) {
            case 'Q':
                if (quitCommand(sockfd, msg) == 0) {
                    close(sockfd);
                    return 0;
                }
                break;
            case 'P':
                if (pwdCommand(sockfd, msg) == 0) {

                }
                break;
            case 'C':
                if (cwdCommand(sockfd, msg) == 0) {

                }
                break;

            case 'I':
                if (infoCommand(sockfd, msg) == 0) {

                }
                break;

            case 'L':
                if (listCommand(sockfd, msg) == 0) {

                }
                break;

            case 'R':
                if (retrCommand(sockfd, msg) == 0) {

                }
                break;
        }

    }
    return 0;
}
